/* allows the user to select:
 * between 2-6 players
 * who is player and who is AI
 * the size of the stage
 */
class WorldClassicSelection extends World {

	/* this worlds assets */
	assets() {
		return [
			"images/stage_empty.png",
			"images/button_up.png",
			"images/button_down.png",
		];
	}

	/* get mode from parent world */
	constructor(data) {
		super(data);

		/* either "classic" or "time" */
		this.mode = data.mode;

	} /* constructor */

	/* initialize */
	start() {

		/* data */
		this.max_players = 2;
		this.ai = [];
		this.returner = {};
		this.returner.ai = this.ai;
		this.returner.stage_x = 3;
		this.returner.stage_y = 3;

		/* everyone is initialized as player */
		for (let i = 0; i < this.max_players; i++) {
			this.ai.push(false);
		}
		
		/* text style */
		let style = new PIXI.TextStyle({
			fill: "#ff0000",
			fontSize: 30,
		});

		/* player buttons */
		this.button_players = [];
		this.text_players = [];

		/* add buttons in row, then calculate their position */
		for (let i = 0; i < this.max_players; i++) {
			this.add_player_button();
		}
		this.relocate_buttons();

		/* update player text */
		this.update_text();

		/* add player button */
		this.button_add = new PIXI.Sprite(
			loader.resources["images/button_up.png"].texture
		);
		this.button_add.anchor = {x:0.5, y:0.5};
		this.button_add.x = width*2/3;
		this.button_add.y = height/3;
		app.stage.addChild(this.button_add);

		/* remove player button */
		this.button_remove = new PIXI.Sprite(
			loader.resources["images/button_down.png"].texture
		);
		this.button_remove.anchor = {x:0.5, y:0.5};
		this.button_remove.x = width/3;
		this.button_remove.y = height/3;
		app.stage.addChild(this.button_remove);

		/* add bigger stage buttons */
		this.button_bigX = new PIXI.Sprite(
			loader.resources["images/button_up.png"].texture
		);
		this.button_bigX.anchor = {x:0.5, y:0.5};
		this.button_bigX.x = width*2/3;
		this.button_bigX.y = height*2/3;
		app.stage.addChild(this.button_bigX);

		this.button_bigY = new PIXI.Sprite(
			loader.resources["images/button_up.png"].texture
		);
		this.button_bigY.anchor = {x:0.5, y:0.5};
		this.button_bigY.x = width*4/5;
		this.button_bigY.y = height*2/3;
		app.stage.addChild(this.button_bigY);

		/* add smaller stage buttons */
		this.button_smallX = new PIXI.Sprite(
			loader.resources["images/button_down.png"].texture
		);
		this.button_smallX.anchor = {x:0.5, y:0.5};
		this.button_smallX.x = width/3;
		this.button_smallX.y = height*2/3;
		app.stage.addChild(this.button_smallX);

		this.button_smallY = new PIXI.Sprite(
			loader.resources["images/button_down.png"].texture
		);
		this.button_smallY.anchor = {x:0.5, y:0.5};
		this.button_smallY.x = width/5;
		this.button_smallY.y = height*2/3;
		app.stage.addChild(this.button_smallY);

		/* add stage size text */
		this.text_size = new PIXI.Text(
			"hello world", {
				fill: "#ff0000",
			});
		this.text_size.anchor = {x:0.5, y:0.5};
		this.text_size.x = width/2;
		this.text_size.y = this.button_bigX.y;
		app.stage.addChild(this.text_size);
		this.update_size();

		/* start game button */
		this.button_start = new PIXI.Sprite(
			loader.resources["images/stage_empty.png"].texture
		);
		this.button_start.anchor = {x:0.5, y:0.5};
		this.button_start.x = width/2;
		this.button_start.y = height*4/5;
		app.stage.addChild(this.button_start);

	} /* start */

	/* handle button touch */
	update() {

		/* handle input */
		for (let e = 0; e < input.length; e++) {

			/* left click */
			if (input[e].which == 1) {

				/* click point */
				let p = new PIXI.Point(
					input[e].offsetX, input[e].offsetY
				);

				/* clicked one of player buttons
				 * change between player and AI
				 */
				for (let i = 0; i < this.button_players.length; i++) {
					if (this.button_players[i].containsPoint(p)) {
						this.ai[i] = !this.ai[i];
						this.update_text();
					}
				}

				/* add new player - max 6 */
				if (this.button_add.containsPoint(p)) {
					if (this.max_players < 6) {
						this.max_players++;
						this.add_player_button();
						this.relocate_buttons();
						this.ai.push(false);
						this.update_text();
					}
				}

				/* remove player - min 2 */
				if (this.button_remove.containsPoint(p)) {
					if (this.max_players > 2) {
						this.max_players--;
						this.remove_player_button();
						this.relocate_buttons();
						this.ai.pop();
					}
				}

				/* bigger stage x */
				if (this.button_bigX.containsPoint(p)) {
					if (this.returner.stage_x < 12) {
						this.returner.stage_x++;
						this.update_size();
					}
				}

				/* bigger stage y */
				if (this.button_bigY.containsPoint(p)) {
					if (this.returner.stage_y < 12) {
						this.returner.stage_y++;
						this.update_size();
					}
				}

				/* smaller stage x */
				if (this.button_smallX.containsPoint(p)) {
					if (this.returner.stage_x > 3) {
						this.returner.stage_x--;
						this.update_size();
					}
				}

				/* smaller stage y */
				if (this.button_smallY.containsPoint(p)) {
					if (this.returner.stage_y > 3) {
						this.returner.stage_y--;
						this.update_size();
					}
				}

				/* start game */
				if (this.button_start.containsPoint(p)) {
					this.returner.max_players = this.max_players;
					this.returner.mode = this.mode;
					return world_list.indexOf(WorldTicTacToe);
				}

			} /* left click */

		} /* handle input */

	} /* update */

	/* for each player update its text */
	update_text() {
		for (let i = 0; i < this.ai.length; i++) {
			if (!this.ai[i]) {
				this.text_players[i].text = "player";
			}
			else {
				this.text_players[i].text = "computer";
			}
		}
	} /* update text */

	/* update stage size */
	update_size() {
		this.text_size.text =
			this.returner.stage_x +"x"
			+this.returner.stage_y;
	} /* update stage size */

	/* add player button in array
	 * must call "relocate" afterwards to
	 * place them all in correct position
	 */
	add_player_button() {

		/* text style */
		let style = new PIXI.TextStyle({
			fill: "#ff0000",
			fontSize: 30,
		});

		/* button */
		let button = new PIXI.Sprite(
			loader.resources["images/stage_empty.png"].texture
		);
		button.anchor = {x:0.5, y:0.5};
		app.stage.addChild(button);
		this.button_players.push(button);
	
		/* text */
		let text = new PIXI.Text(
			"playerxxxx", style);
		text.anchor = {x:0.5, y:0.5};
		app.stage.addChild(text);
		this.text_players.push(text);

	} /* add player button */

	/* remove player button,
	 * must call relocate afterwards
	 */
	remove_player_button() {
		app.stage.removeChild(
			this.button_players[this.button_players.length-1]
		);
		app.stage.removeChild(
			this.text_players[this.text_players.length-1]
		);
		this.button_players.pop();
		this.text_players.pop();
	} /* remove player button */

	/* places all player buttons in the correct position */
	relocate_buttons() {

		for (let i = 0; i < this.max_players; i++) {

			/* button */
			let button = this.button_players[i];
			button.x = width*(i+1)/(this.max_players+1);
			button.y = 50;
	
			/* text */
			let text = this.text_players[i];
			text.x = button.x;
			text.y = button.y;

		}

	} /* relocate buttons */

} /* world classic selection */


/* describes a world with two buttons
 * classic game and
 * time attack
 */
class WorldMenu extends World {

	/* main menu isn't so fancy */
	assets() {
		return [
			"images/stage_empty.png",
		];
	}

	/* initialize everything */
	start() {

		/* object to transfer data between worlds */
		this.returner = {};

		/* title style */
		let titleStyle = new PIXI.TextStyle({
			fill: "#ff0000",
			fontSize: 70,
			fontWeight: "bold",
		});

		/* title text */
		this.text_title = new PIXI.Text(
			"Mul-Tic-Toe!", titleStyle);
		this.text_title.anchor = {x:0.5, y:1};
		this.text_title.x = width/2;
		this.text_title.y = 100;
		app.stage.addChild(this.text_title);

		/* text style */
		let style = new PIXI.TextStyle({
			fill: "#ff0000",
			fontSize: 30,
		});

		/* play classic button */
		this.button_start = new PIXI.Sprite(
			loader.resources["images/stage_empty.png"].texture
		);
		this.button_start.anchor = {x:0.5, y:0.5};
		this.button_start.x = width/2;
		this.button_start.y = height *2/4;
		app.stage.addChild(this.button_start);

		this.text_friend_start = new PIXI.Text(
			"classic", style);
		this.text_friend_start.anchor = {x:0.5, y:0.5};
		this.text_friend_start.x = this.button_start.x;
		this.text_friend_start.y = this.button_start.y;
		app.stage.addChild(this.text_friend_start);

		/* play time attack button */
		this.button_time = new PIXI.Sprite(
			loader.resources["images/stage_empty.png"].texture
		);
		this.button_time.anchor = {x:0.5, y:0.5};
		this.button_time.x = width/2;
		this.button_time.y = height *3/4;
		app.stage.addChild(this.button_time);

		this.text_time = new PIXI.Text(
			"time attack", style);
		this.text_time.anchor = {x:0.5, y:0.5};
		this.text_time.x = this.button_time.x;
		this.text_time.y = this.button_time.y;
		app.stage.addChild(this.text_time);

	} /* constructor */

	/* handle button pressing */
	update() {

		/* handle input */
		for (let e = 0; e < input.length; e++) {

			/* left click */
			if (input[e].which == 1) {

				/* click point */
				let p = new PIXI.Point(
					input[e].offsetX, input[e].offsetY
				);

				/* start classic */
				if (this.button_start.containsPoint(p)) {
					this.returner.mode = "classic";
					return world_list.indexOf(WorldClassicSelection);
				}

				/* start time */
				if (this.button_time.containsPoint(p)) {
					this.returner.mode = "time";
					return world_list.indexOf(WorldClassicSelection);
				}


			} /* left click */

		} /* handle input */

	} /* update */

} /* world menu */
/* game of tic-tac-toe with:
 * custom size of stage
 * custom number of players
 * mixed AI and players
 */
class WorldTicTacToe extends World {

	/* assets for this world */
	assets() {
		return [
			"images/stage_empty.png",
			"images/stage_p1.png",
			"images/stage_p2.png",
			"images/stage_p3.png",
			"images/stage_p4.png",
			"images/stage_p5.png",
			"images/stage_p6.png",
			"images/button_quit.png",
			"images/button_restart.png",
		];
	}
	
	/* runs before assets are initialized,
	 * used to get data from previous world
	 */
	constructor(data) {
		super(data);

		/* save who is AI and who is player */
		this.ai = data.ai;
		this.max_players = data.max_players;
		this.returner = data;
		this.stage_x = data.stage_x;
		this.stage_y = data.stage_y;
		this.mode = data.mode;

	} /* constructor */

	/* initialization */
	start() {

		/* text style */
		this.text_style = new PIXI.TextStyle({
			fill: "#ff0000",
			fontSize: 40,
		});

		/* game data */
		this.current_player = this.max_players;
		this.playing = true;

		/* stage */
		this.stage = [];

		for (let i = 0; i < this.stage_x *this.stage_y; i++) {
			this.stage.push(0);
		}

		/* stage appearence */
		this.stage_sprite = [];
		for (let i = 0; i < this.stage.length; i++) {
			this.stage_sprite[i] = new PIXI.Sprite(
				loader.resources["images/stage_empty.png"].texture
			);
			this.stage_sprite[i].x = width /(this.stage_x+1) *(i%this.stage_x+1);
			this.stage_sprite[i].y = height /(this.stage_y+1) *(Math.floor(i/this.stage_x)+1);
			this.stage_sprite[i].anchor = {x:0.5, y:0.5};
			this.stage_sprite[i].hitArea = new PIXI.Rectangle(0, 0, 100, 100);
			app.stage.addChild(this.stage_sprite[i]);
		}

		/* current player text */
		this.text_cplayer = new PIXI.Text("Hello World", 
			this.text_style);
		this.text_cplayer.anchor = {x:0.5, y:1.0};
		this.text_cplayer.x = width/2;
		this.text_cplayer.y = height;
		app.stage.addChild(this.text_cplayer);

		/* restart button (visible when game ends) */
		this.button_restart = new PIXI.Sprite(
			loader.resources["images/button_restart.png"].texture
		);
		this.button_restart.visible = false;
		app.stage.addChild(this.button_restart);

		/* quit button */
		this.button_quit = new PIXI.Sprite(
			loader.resources["images/button_quit.png"].texture
		);
		this.button_quit.anchor = {x:1, y:0};
		this.button_quit.x = width;
		app.stage.addChild(this.button_quit);

		/* AI timer */
		this.ai_timer = 0;
		this.ai_max = 40;

		/* time attack mode depends on value of timer:
		 * -1: disabled
		 * positive: enabled
		 */
		this.timer = -1;
		this.max_timer = 90;
		if (this.mode === "time") {
			this.timer = this.max_timer;

			this.text_timer = new PIXI.Text(
				"timer", this.text_style);
			this.text_timer.anchor = {x:0.5, y:0};
			this.text_timer.x = width/2;
			app.stage.addChild(this.text_timer);
		}

		/* start game */
		this.end_turn();
	}

	/* player touched stage,
	 * if touched on empty part,
	 * 	fill it and move to next player
	 */
	update() {

		/* timer on time attack */
		if (this.playing && this.timer > 0) {

			this.timer--;
			this.text_timer.text = "time: " +this.timer;

			if (this.timer == 0) {
				this.end_turn();
			}

		}

		/* AI */
		if (this.playing && this.ai[this.current_player-1]) {

			/* advance timer */
			this.ai_timer++;

			/* time to make a move */
			if (this.ai_timer >= this.ai_max) {

				/* find all empty cells */
				let empty_stage = [];
				for (let i = 0; i < this.stage.length; i++) {
					if (this.stage[i] == 0) {
						empty_stage.push(i);
					}
				}

				/* pick one of the cells */
				let target = empty_stage[Math.floor( Math.random() *empty_stage.length )];

				/* make move
				 * if for any reason it picks
				 * an occupied cell, it will restart
				 */
				this.make_move(target);

				/* restart timer for next bot */
				this.ai_timer = 0;

			} /* make move */

			/* ignore all input and updates while AI is playing */
			return;

		} /* AI */

		/* handle input */
		for (let e = 0; e < input.length; e++) {

			/* mouse left click */
			if (input[e].which == 1) {

				/* clicking point */
				let p = new PIXI.Point(input[e].offsetX, input[e].offsetY);

				/* clicked on quit button */
				if (this.button_quit.containsPoint(p)) {

					/* quit to menu */
					return world_list.indexOf(WorldMenu);

				} /* quit menu */
			
				/* game is finished
				 * touch only restart and quit button
				 */
				if (!this.playing) {
				
					/* restart button */
					if (this.button_restart.containsPoint(p)) {
	
						/* restart this world */
						return world_list.indexOf(WorldTicTacToe);
	
					} /* restart button */

					/* stop any other input */
					break;

				}

				/* through all buttons */
				for (let i = 0; i < this.stage_sprite.length; i++) {

					/* touched button */
					if (this.stage_sprite[i].containsPoint(p)) {

						this.make_move(i);

						/* touch only one button at a time */
						break;

					} /* touched button */

				} /* find button */

			} /* left click */

		} /* handle input */

	} /* update */

	/* check if someone won the game */
	check_victory() {

		/* divine the map into smaller 3x3 boxes, and
		 * check them individually
		 */
		for (let x = 0; x <= this.stage_x-3; x++)
		for (let y = 0; y <= this.stage_y-3; y++) {

			/* save result as it's the winner or draw
			 * if it's 0, ignore it
			 */
			let res = this.check_box(x, y);
			if (res != 0) {
				return res;
			}
		}
		
		/* no-one won yet */
		return 0;

	} /* check victory */

	/* checks a 3x3 box on the map, starting from x,y
	 * to find a winner
	 */
	check_box(x, y) {

		let stage = this.stage;
		let sp = x+(y*this.stage_x);
		let rows = this.stage_x;

		/* check rows-lines */
		for (let i = 0; i < 3; i++) {

			let hor = sp+(i*rows);

			/* horizontal */
			if (stage[hor] == stage[hor+1]
			&& stage[hor+1] == stage[hor+2]
			&& stage[hor] != 0) {
				return stage[hor];
			}

			let ver = sp+i;

			/* vertical */
			if (stage[ver] == stage[ver+rows]
			&& stage[ver+rows] == stage[ver+rows*2]
			&& stage[ver] != 0) {
				return stage[ver];
			}
		}

		/* diagonal */
		if (stage[sp] == stage[sp+rows+1]
		&& stage[sp+rows+1] == stage[sp+rows*2+2]
		&& stage[sp] != 0) {
			return stage[sp];
		}

		/* other diagonal */
		if (stage[sp+2] == stage[sp+rows+1]
		&& stage[sp+rows+1] == stage[sp+rows*2]
		&& stage[sp+2] != 0) {
			return stage[sp+2];
		}

		/* if all cells are filled, and there is no winner
		 * it is a draw
		 */
		let draw = true;
		for (let i = 0; i < this.stage_x *this.stage_y; i++) {
			if (this.stage[i] == 0) {
				draw = false;
				break;
			}
		}

		if (draw) {
			return -1;
		}

		/* no-one won yet */
		return 0;

	} /* check box */

	/* ends current player's turn */
	end_turn() {

		/* move to the next player */
		if (++this.current_player > this.max_players) {
			this.current_player = 1;
		}

		/* depending on the value of victory:
		 * -1: draw
		 *  0: game still running
		 *  >0: player with that number won
		 */
		let v = this.check_victory();
		if (v > 0) {
			this.playing = false;
			this.text_cplayer.text =
				"player " +v +" won!";
			this.button_restart.visible = true;
			return;
		}
		else
		if (v == -1) {
			this.playing = false;
			this.text_cplayer.text = "Draw!";
			this.button_restart.visible = true;
			return;
		}

		/* update current player text */
		let txt_player = this.ai[this.current_player-1] ? "computer" : "player";
		this.text_cplayer.text = txt_player +": " +this.current_player;

		/* in time attack, reset timer for new player */
		if (this.timer >= 0) {
			this.timer = this.max_timer;
		}

	} /* end turn */
	
	/* if i is empty cell,
	 * mark it with current player and return true
	 * else return false
	 */
	make_move(i) {

		/* if square is empty, 
		 * fill it with current player's index,
		 * end turn
		 */
		if (this.stage[i] == 0) {
			this.stage[i] = this.current_player;
			this.stage_sprite[i].texture =
				loader.resources["images/stage_p" 
					+this.current_player +".png"].texture;
			this.end_turn();
			return true;
		}

		/* not cell changed */
		return false;

	} /* make move */

} /* world tic-tac-toe */
let world_list = [
	WorldMenu,
	WorldClassicSelection,
	WorldTicTacToe,
];
